// MainWindow.cs created with MonoDevelop
// User: kpr at 16:09 19.10.2007
//
// To change standard headers go to Edit->Preferences->Coding->Standard Headers
//
using System;
using System.IO;
using System.Collections.Generic;
using Gtk;
using Designer;

public partial class MainWindow: Gtk.Window
{	
	public MainWindow (): base (Gtk.WindowType.Toplevel)
	{
		Build ();
		Init();
		//TODO: Game settings window
		System.AppDomain.CurrentDomain.UnhandledException += UnhandledException;
	}
	protected void UnhandledException(object o, System.UnhandledExceptionEventArgs args)
	{
		Exception e = (Exception)args.ExceptionObject;
		MessageDialog message = new MessageDialog(ResourceMgr.Instance.Win, DialogFlags.Modal, MessageType.Error, ButtonsType.None, false, "");
		message.Title = "Ausnahme";
		message.Text = "Es ist eine Ausnahme aufgetreten:\n"+e.Message+"\n"+e.StackTrace;
		message.AddButton(Stock.Ok, ResponseType.Ok);
		message.Run();
		message.Destroy();
	}
	TreeStore ResStore;
	public Dictionary<string,TreeIter> ResGroups = new Dictionary<string,TreeIter>();
	public Dictionary<string,GtkSourceView.SourceLanguage> SourceLangs = new Dictionary<string,GtkSourceView.SourceLanguage>();
	public Dictionary<Resource,iEditTab> editTabsList = new Dictionary<Resource,iEditTab>();
	public Notebook editTabs;
	Menu resEditPop;
	Resource resEditPopRes;
	string tempDir;
	string projectname;
	public string projectName
	{
		get
		{
			return projectname;
		}
		set
		{
			projectname = value;
			Title = "GamadusDesigner - "+projectName+(unsavedChanges?" *":"");
		}
	}
	
	string saveFilename = null;
	bool unsavedchanges = false;
	public bool unsavedChanges
	{
		get
		{
			return unsavedchanges;
		}
		set
		{
			unsavedchanges = value;
			if(value == false)
				Title = "GamadusDesigner - "+projectName;
			else
				Title = "GamadusDesigner - "+projectName+" *";
		}
	}
	
	bool Init()
	{
		DirectoryInfo dir;
		if(System.IO.Path.DirectorySeparatorChar == '\\')
		{
			Console.WriteLine("Using Windows");
			dir = new DirectoryInfo(System.IO.Path.GetDirectoryName(System.IO.Path.Combine(System.Environment.CurrentDirectory,System.Environment.GetCommandLineArgs()[0])));
		}
		else
		{
			Console.WriteLine("Using Unix");
			dir = new DirectoryInfo(System.IO.Path.GetDirectoryName(System.Environment.GetCommandLineArgs()[0]));
		}
		Console.WriteLine(dir.FullName);
		tempDir = System.IO.Path.Combine(dir.FullName, "temp")+System.IO.Path.DirectorySeparatorChar;
		Console.WriteLine("Temp dir: "+tempDir);
		//get Builder dir
		BuildGame.Instance.builderExe = System.IO.Path.Combine(dir.FullName, "Builder.exe");
		if(!Directory.Exists(tempDir))
		{
			Directory.CreateDirectory(tempDir);
			Console.WriteLine("Temp dir created");
		}
		if(!File.Exists(BuildGame.Instance.builderExe))
			Console.WriteLine("TODO: Builder couldn't be found at "+BuildGame.Instance.builderExe);
		
		
		//create language specs and initialize gtksourceview
		DirectoryInfo homedir = new DirectoryInfo(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop));
		string langspecs = homedir.Parent.FullName;
		langspecs = System.IO.Path.Combine(langspecs,".gnome2");
		if(!Directory.Exists(langspecs))
		Directory.CreateDirectory(langspecs);
		langspecs = System.IO.Path.Combine(langspecs,"gtksourceview-1.0");
		if(!Directory.Exists(langspecs))
		Directory.CreateDirectory(langspecs);
		langspecs = System.IO.Path.Combine(langspecs,"language-specs");
		if(!Directory.Exists(langspecs))
		Directory.CreateDirectory(langspecs);
		langspecs += System.IO.Path.DirectorySeparatorChar;
		Console.WriteLine("Writing language specifications to "+langspecs);
		StreamWriter sw;
		//write gml.lang
		sw = new StreamWriter(langspecs+"gml.lang");
		sw.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE language SYSTEM \"language.dtd\">");
		sw.WriteLine("<language _name=\"GML\" version=\"1.0\" _section=\"Sources\" mimetypes=\"text/x-gml\">"+
		"<escape-char>\\</escape-char>"+
		"<string _name = \"String\" style = \"String\" end-at-line-end = \"TRUE\">"+
		"<start-regex>&quot;</start-regex>"+
		"<end-regex>&quot;</end-regex>"+
		"</string>"+
		"<string _name = \"String 2\" style = \"String\" end-at-line-end = \"TRUE\">"+
		"<start-regex>&apos;</start-regex>"+
		"<end-regex>&apos;</end-regex>"+
		"</string>"+
		"<line-comment _name = \"Line Comment\" style= \"Comment\">"+
		"<start-regex>//</start-regex>"+
		"</line-comment>"+
		"<block-comment _name = \"Block Comment\" style = \"Comment\">"+
		"<start-regex>/\\*</start-regex>"+
        "<end-regex>\\*/</end-regex>"+
		"</block-comment>"+
		"<keyword-list _name = \"Keywords\" style = \"Keyword\" case-sensitive=\"TRUE\">"+
		"<keyword>if</keyword>"+
		"<keyword>global.</keyword>"+
		"<keyword>var</keyword>"+
		"<keyword>while</keyword>"+
		"<keyword>do</keyword>"+
		"<keyword>until</keyword>"+
		"<keyword>else</keyword>"+
		"<keyword>repeat</keyword>"+
		"<keyword>for</keyword>"+
		"<keyword>switch</keyword>"+
		"<keyword>break</keyword>"+
		"<keyword>case</keyword>"+
		"<keyword>default</keyword>"+
		"<keyword>continue</keyword>"+
		"<keyword>exit</keyword>"+
		"<keyword>return</keyword>"+
		"<keyword>with</keyword>"+
		"<keyword>true</keyword>"+
		"<keyword>false</keyword>"+
		"<keyword>then</keyword>"+
		"</keyword-list>"+
		"</language>");
		sw.Close();
		//write gool.lang
		sw = new StreamWriter(langspecs+"gool.lang");
		sw.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE language SYSTEM \"language.dtd\">");
		sw.WriteLine("<language _name=\"GOOL\" version=\"1.0\" _section=\"Sources\" mimetypes=\"text/x-gool\">");
		sw.WriteLine("</language>");
		sw.Close();
		
		GtkSourceView.SourceLanguagesManager mgr = new GtkSourceView.SourceLanguagesManager();
		foreach(GtkSourceView.SourceLanguage lang in mgr.AvailableLanguages)
		{
			if(lang.Name == "GML")
				SourceLangs["GML"] = lang;
			else if(lang.Name == "GOOL")
				SourceLangs["GOOL"] = lang;
		}
		if(!SourceLangs.ContainsKey("GML"))
			throw new FileNotFoundException("No GML Syntax definiton found ("+langspecs+"gml.lang)");
		if(!SourceLangs.ContainsKey("GOOL"))
			throw new FileNotFoundException("No GOOL Syntax definiton found ("+langspecs+"gool.lang)");
		editTabs = this.cEditTabs;
		//initialize resource tree view
		TreeViewColumn resourceColumn = new TreeViewColumn ();
		resourceColumn.Title = "Ressourcen";
		CellRendererText resourceNameCell = new CellRendererText ();
		resourceNameCell.Editable = true;
		resourceNameCell.EditingStarted += OnResourceNameCellEditingStarted;
		resourceNameCell.Edited += OnResourceNameCellEdited;
		resourceColumn.PackStart (resourceNameCell, true);
		resourceColumn.AddAttribute (resourceNameCell, "text", 0);
		this.wResTree.AppendColumn (resourceColumn);
		this.wResTree.EnableTreeLines = true;
		//TODO:this.wResTree.Reorderable = true;
		ResStore = new TreeStore (typeof (string));
		this.wResTree.Model = ResStore;
		
		//initialize res-tree context menu
		resEditPop = new Menu();
		MenuItem resRemove = new MenuItem("Entfernen");
		resRemove.Activated += OnResEditPopRemove;
        resEditPop.Append(resRemove);
		
		ResourceMgr.Instance.Init(wResTree, this);
		ResourceMgr.Instance.tempDir = this.tempDir;
		
		ResourceMgr.Instance.NewProject();
		
		return true;
	}
	
	protected virtual void OnResEditPopRemove(object o, EventArgs args)
	{
		Gtk.MessageDialog message = new MessageDialog(this, DialogFlags.Modal, MessageType.Question, ButtonsType.None, false, "");
		message.Text = "Soll diese Ressource wirklich gelöscht werden? Dieser Vorgang kann nicht rückgängig gemacht werden!";
		if(resEditPopRes is Obj)
			message.Text += " Falls Instanzen dieses Objekts in einer Ebene sind, werden diese ebenfalls gelöscht.";
		message.Title = "Wirklich löschen?";
		message.AddButton(Stock.Yes, ResponseType.Yes);
		message.AddButton(Stock.No, ResponseType.No);
		message.AddButton(Stock.Cancel, ResponseType.Cancel);
		ResponseType RetVal = (ResponseType)message.Run();
		message.Destroy();
		if(RetVal == ResponseType.Yes)
			ResourceMgr.Instance.DeleteResource(resEditPopRes);
	}
	
	public void ReloadEditTabs()
	{
		foreach(KeyValuePair<Resource,iEditTab> entry in editTabsList)
		{
			iEditTab tabEntry = (iEditTab) entry.Value;
			tabEntry.Reload();
		}
	}
	public void ShowEditTab(Resource edit)
	{
		System.Collections.Generic.List<string> editable = new System.Collections.Generic.List<string>();
		editable.Add("Object");
		editable.Add("Code");
		editable.Add("Room");
		editable.Add("Font");
		editable.Add("Event");
		if (!editable.Contains(edit.GetResType()))
		{
			Console.WriteLine("[NOT IMPLEMENTED] Edit resource of type "+edit.GetResType());
			return;
		}
        iEditTab objedit = null;
        EventType preselectEvent = EventType.Null;
        if(edit.GetResType() == "Event")
        {
        	preselectEvent = (edit as Event).type;
        	edit = (edit as Event).Parent;
        }
        if(editTabsList.ContainsKey(edit))
        {
        	Console.WriteLine("Tab already open");
        	objedit = (iEditTab) editTabsList[edit];
        }
        else
        {
        	if(edit.GetResType() == "Object")
        	{
        		if(preselectEvent != EventType.Null)
        			objedit = new ObjectEditTab((Obj)edit, preselectEvent);
        		else
        			objedit = new ObjectEditTab((Obj) edit);
        	}
        	else if(edit.GetResType() == "Code")
        		objedit = new CodeEditTab((Code) edit);
        	else if(edit.GetResType() == "Room")
        		objedit = new RoomEditTab((Room) edit);
        	else if(edit.GetResType() == "Font")
        		objedit = new FontEditTab((Font) edit);
        	Console.WriteLine("Creating tab");
        	editTabsList.Add(edit, objedit);
        }
        editTabs.CurrentPage = objedit.GetPosition();
	}
	
	public void CloseEditTab(Resource edit, bool save)
	{
		Console.WriteLine("Closing tab");
		if(!editTabsList.ContainsKey(edit)) return;
		iEditTab tab = (iEditTab) editTabsList[edit];
		int remIndex = ((iEditTab)editTabsList[edit]).GetPosition();
		Console.WriteLine("Removing at "+remIndex.ToString());
		foreach(KeyValuePair<Resource,iEditTab> entry in editTabsList)
		{
			iEditTab tabEntry = (iEditTab) entry.Value;
			if(save)
				tabEntry.Reload(); //reload on closeedittab
			if(tabEntry.GetPosition() > remIndex)
				tabEntry.SetPosition(tabEntry.GetPosition() - 1);
		}
		editTabsList.Remove(edit);
		tab.Close(save);
		unsavedChanges = save ? true : unsavedChanges;
	}
	public void Save(bool newFile)
	{
		string file;
		if(!newFile && saveFilename != null)
			file = saveFilename;
		else
		{
			//newFile true: choose a file, even if there is loaded/saved filename
			FileChooserDialog Fcd = new FileChooserDialog ("Projekt speichern", null, FileChooserAction.Save);
			Fcd.AddButton(Stock.Cancel, ResponseType.Cancel);
			Fcd.AddButton(Stock.Save, ResponseType.Ok);
			Fcd.Filter = new FileFilter();
			Fcd.Filter.AddPattern("*.gpf");
			ResponseType RetVal = (ResponseType)Fcd.Run();
			if (RetVal == ResponseType.Ok)
			{
				file = Fcd.Filename.EndsWith(".gpf")?Fcd.Filename:Fcd.Filename+".gpf";
				if(File.Exists(file))
				{
					Gtk.MessageDialog message = new MessageDialog(this, DialogFlags.Modal, MessageType.Question, ButtonsType.None, false, "");
					message.Text = "Die Datei "+new System.IO.FileInfo(file).Name+" existiert bereits, soll sie überschrieben werden?";
					message.Title = "Datei überschreiben?";
					message.AddButton(Stock.Yes, ResponseType.Yes);
					message.AddButton(Stock.No, ResponseType.No);
					RetVal = (ResponseType)message.Run();
					message.Destroy();
					if(RetVal != ResponseType.Yes)
					{
						Fcd.Destroy();
						return;
					}
				}
			}
			else
			{
				Fcd.Destroy();
				return;
			}
			Fcd.Destroy();
		}
		saveFilename = file;
		ResourceMgr.Instance.SaveProject(file);
		projectName = saveFilename.Substring(saveFilename.LastIndexOf(System.IO.Path.DirectorySeparatorChar)+1);
		unsavedChanges = false;

	}
	public void Load()
	{
		if(!ResourceMgr.Instance.NewProject()) return;
	  	FileChooserDialog Fcd = new FileChooserDialog ("Projekt öffnen", null, FileChooserAction.Open);
	  	Fcd.AddButton(Stock.Cancel, ResponseType.Cancel);
	  	Fcd.AddButton(Stock.Open, ResponseType.Ok);
	  	Fcd.Filter = new FileFilter();
	  	Fcd.Filter.AddPattern("*.gpf");
 		Fcd.SelectMultiple = false;
	  	ResponseType RetVal = (ResponseType)Fcd.Run();
	  	if (RetVal == ResponseType.Ok)
	  	{
	  		saveFilename=Fcd.Filename;
	  		ResourceMgr.Instance.LoadProject(saveFilename);
	  		projectName = saveFilename.Substring(saveFilename.LastIndexOf(System.IO.Path.DirectorySeparatorChar)+1);
	  		unsavedChanges = false;
	  	}
	  	Fcd.Destroy();
	  	
	}
	protected void OnDeleteEvent (object sender, DeleteEventArgs a)
	{
		if(ResourceMgr.Instance.NewProject())
		{
			Console.WriteLine("Shutting down designer...");
			Application.Quit ();
			a.RetVal = true;
		}
	}
	
	protected virtual void OnOpenProject(object sender, System.EventArgs e)
	{
		Load();
	}

	protected virtual void OnSaveProject(object sender, System.EventArgs e)
	{
		Save(false);
	}

	protected virtual void OnSaveProjectAs(object sender, System.EventArgs e)
	{
		Save(true);
	}
	
	DateTime editstart;
	protected void OnResourceNameCellEdited(object o, Gtk.EditedArgs args)
	{
		TimeSpan edittime = DateTime.Now - editstart;
		//Console.WriteLine(edittime.Seconds+"s, "+edittime.Milliseconds+"ms");
		if(edittime.Seconds == 0 && edittime.Milliseconds < 100)
		{
			TreePath activatedPath = new TreePath(args.Path); 
			if(activatedPath.Depth == 1)
				return;
//			TreeIter iter;
//			ResStore.GetIter(out iter, activatedPath);
			Resource activatedObject = ResourceMgr.Instance.GetResourceByPath(activatedPath);
			Console.WriteLine("Selected "+activatedObject.GetResType()+" "+activatedObject.Name);
			ShowEditTab(activatedObject);
			return;
		}
		else
		{
			if(new Gtk.TreePath (args.Path).Depth < 2)
				return;
			Resource ress = ResourceMgr.Instance.GetResourceByPath(new TreePath(args.Path));
			if(ResNameEntry.IsValid(args.NewText))
			{
				Console.WriteLine("Renaming "+ress.Name+" to "+args.NewText);
				ResourceMgr.Instance.ChangeResourceName(ress, args.NewText);
				ReloadEditTabs();
				unsavedChanges = true;
			}
		}
	}
	protected void OnResourceNameCellEditingStarted(object o, EditingStartedArgs args)
	{
		if(new TreePath(args.Path).Depth < 2)
		{
			Entry ent = (Entry)args.Args[0];
			ent.Hide();
			return;
		}
		editstart = DateTime.Now;
	}

	protected virtual void OnNewProject (object sender, System.EventArgs e)
	{
		ResourceMgr.Instance.NewProject();
	}

	protected virtual void OnNewFont (object sender, System.EventArgs e)
	{
		ResourceMgr.Instance.AddResource("Font");
	}

	protected virtual void OnNewObject (object sender, System.EventArgs e)
	{
		ResourceMgr.Instance.AddResource("Object");
	}

	protected virtual void OnNewRoom (object sender, System.EventArgs e)
	{
		ResourceMgr.Instance.AddResource("Room");
	}

	protected virtual void OnWResTreeButtonReleaseEvent (object o, Gtk.ButtonReleaseEventArgs args)
	{
		Gdk.EventButton eventbutton = args.Event;
		if(eventbutton.Button != 3)	return;
//		TreePath path = new TreePath();
//		this.wResTree.GetPathAtPos((int)eventbutton.X, (int)eventbutton.Y, out path);
//		if(path.Depth < 2) return;
//		TreeIter iter;
//		this.ResStore.GetIter(out iter, path);
		Resource res = GetResourceAtTreePos((int)eventbutton.X, (int)eventbutton.Y);
		if(res == null) return;
		Console.WriteLine("Show context menu for "+res.Name);
		resEditPopRes = res;
		resEditPop.Popup();
		resEditPop.ShowAll();
	}
	
	Resource GetResourceAtTreePos(int x, int y)
	{
		TreePath path = new TreePath();
		this.wResTree.GetPathAtPos(x, y, out path);
		if(path.Depth < 2)
			return null;
		TreeIter iter;
		this.ResStore.GetIter(out iter, path);
		Console.WriteLine(iter.Stamp);
		return ResourceMgr.Instance.GetResourceByIter(iter);
	}

	protected virtual void OnTestGame (object sender, System.EventArgs e)
	{
		ResourceMgr.Instance.SaveProject(tempDir+"testing.zip");
		bool success = BuildGame.Instance.Build(tempDir+"testing.zip", tempDir+"testing.exe");
		File.Delete(tempDir+"testing.zip");
		if(success)
		{
			BuildGame.Instance.RunGame(tempDir+"testing.exe");
			File.Delete(tempDir+"testing.exe");
			foreach(string f in Directory.GetFiles(System.IO.Path.Combine(tempDir,"gamadusgame")))
			{
				File.Delete(f);
			}
			Directory.Delete(System.IO.Path.Combine(tempDir,"gamadusgame"));
		}
	}

	protected virtual void OnBuildGame (object sender, System.EventArgs e)
	{
		FileChooserDialog Fcd = new FileChooserDialog ("Spiel erstellen", null, FileChooserAction.Save);
		Fcd.AddButton(Stock.Cancel, ResponseType.Cancel);
		Fcd.AddButton(Stock.Save, ResponseType.Ok);
		Fcd.Filter = new FileFilter();
		Fcd.Filter.AddPattern("*");
		if(projectName == "Neues Projekt")
		{
			Fcd.CurrentName = "game.exe";
		}
		else
		{
			FileInfo fi = new FileInfo(saveFilename);
			Fcd.CurrentName = fi.Name.EndsWith(".gpf")?fi.Name.Substring(0,fi.Name.Length-4)+".exe":fi.Name;
		}
		ResponseType RetVal = (ResponseType)Fcd.Run();
		if (RetVal == ResponseType.Ok)
		{
			string file = Fcd.Filename;
			file = file.EndsWith(".exe")?file:file+".exe";
			if(File.Exists(file))
			{
				Gtk.MessageDialog message = new MessageDialog(this, DialogFlags.Modal, MessageType.Question, ButtonsType.None, false, "");
				message.Text = "Die Datei "+new System.IO.FileInfo(file).Name+" existiert bereits, soll sie überschrieben werden?";
				message.Title = "Datei überschreiben?";
				message.AddButton(Stock.Yes, ResponseType.Yes);
				message.AddButton(Stock.No, ResponseType.No);
				RetVal = (ResponseType)message.Run();
				message.Destroy();
				if(RetVal != ResponseType.Yes)
				{
					Fcd.Destroy();
					return;
				}
			}
			ResourceMgr.Instance.SaveProject(tempDir+"testing.zip");
			BuildGame.Instance.Build(tempDir+"testing.zip",Fcd.Filename);
			File.Delete(tempDir+"testing.zip");
			}
			Fcd.Destroy();
			return;
	}
}